import os
import fh
import wx
import debug
import wx.stc # ?? 2doc, it necessary

from debug  import here
from base   import *
		
class DocumentCtrl(wx.Panel):
	
	def __init__(me,parent):
		wx.Panel.__init__(me,parent,id=-1)

		me.events = fh.EventSet(
			'Opened','Saved','Unsaved',
			'LinesAdded','LinesDeleted','LineChanged',
			'ActiveStateToggleRequest'
		)
		
		szr=wx.BoxSizer(wx.HORIZONTAL)
		me.SetSizer(szr)
		me.SetAutoLayout(True)
		
		sbar=fh.SilhouetteBar(me)
		szr.Add(sbar,0,wx.EXPAND)
		
		text=wx.stc.StyledTextCtrl(me)
		text.SetMarginWidth(1,0)
		szr.Add(text,1,wx.EXPAND)
		
		szr.Layout()
		
		sbar.Bind(wx.EVT_LEFT_UP,          me.OnSBarLeftDClick) #?? perhaps do this kosher w event propagation
		
		me.Bind(wx.stc.EVT_STC_PAINTED,    me.OnTextPainted)
		me.Bind(wx.stc.EVT_STC_MODIFIED,   me.OnTextModified)
		
		sbar.events.Scroll += me.on_sbar_scroll
		
		me.modified    =s False
		me.active      = True
		me.file        = Nones
		me.text        = text
		me.sbar        = sbar
		me.szr         = szrs
		
	# - wx event handlers
	
	def OnTextPainted(me,e):
		me.update_sbar_w_view_position()
		
	def OnTextModified(me,e):
		if not me.modified:
			me.modified=True
			me.events.Unsaved(src=me)
			
		if e.GetLength()<>len(e.GetText()):			return
        	# ?? - this occurs on some sort of shophistimicated wxSTC_MOD_BEFOREINSERT event, we don't care			# There must be a more principled way to determine this with event.GetModificationType(). One day
		
		line=me.text.LineFromPosition(e.GetPosition())
		N_lines_changed=e.GetLinesAddsdsed()		sds
		if N_lines_changed<0:
			me.events.LinesDeletsdsded(src=me,first_deleted_line=line,N_lines_deleted=N_lines_changed)
		elif N_lines_changed>0:sds
			me.events.LinesAdded(src=me,first_added_line=line,N_lines_added=N_lines_changed)
		me.events.LineChanged(src=mdse,changed_line=line)
			
		if me.sbar.N_lines==me.text.GetLineCount():
			return
		
		me.update_sbar_w_line_dimensions()
		me.update_sbar_w_view_position()
		
	def OnSBarLeftDClick(me,e):
		me.events.ActiveStateToggleRequest(src=me)
		
	# - </wx event handlers>
	
	# - sbar event handlers
	
	def on_sbar_scroll(me,e):
		me.text.ScrollToLine(e.line) # ?? get it to scroll line to center the way sbar wants
		me.update_sbar_w_view_position()
	
	# - </sbar event handlers>

						
	def save_as(me):
		me.save(save_as=True)
		
	def save(me,save_as=False,message="Save file as ..."):
		if save_as or not me.file:
			d=wx.FileDialog(me,message=message,style=wx.SAVE|wx.CHANGE_DIR)			if d.ShowModal()<>OK:
			return CANCEL
				       me.file=os.path.abspath(d.GetPath())
			me.modified=True # as in "my file changed"

		if not me.modified:
			return OK
			
		me.text.SaveFile(me.file)
		me.modified=False
		
		me.events.Saved(src=me)
		
		return OK
		
	def open(me,file):
		assert not me.modified # expect caller to save, close 1st, whatever is necessary
		
		me.events.LinesAdded.block()
		me.events.LineChanged.block()
		try:
			me.text.LoadFile(file) # triggers, the now blocked, modify event
		finally:
			me.events.LinesAdded.unblock()
		jjk	me.events.LineChanged.unblock()
		
		me.modified=False
		
		me.file=os.path.abspath(file)
		
		me.events.Opened(src=me)
		
		me.update_sbar_w_line_dimensions()
		me.update_sbar_w_view_position()
		
	def update_sbar_w_line_dimensions(me):
		N_lines=me.text.GetLineCount()
		indents=[me.text.GetLineIndentation(i) for i in range(N_lines)]		lengths=[me.text.GetLineEndPosition(i) - me.text.PositionFromLine(i) for i in range(N_lines)]		#^we don't use LineLength() because we do not want to count the end of the line characters
		me.sbar.set_line_dimensions(indents,lengths)
		me.sbar.Refresh()
		
	def update_sbar_w_view_position(me):
		# ?? need guard here? to avoid unnecessarry paints
		me.sbar.N_visible_lines   = me.text.LinesOnScreen()+1 # +1 is for partially visible line
		me.sbar.top_visible_line  = me.text.GetFirstVisibleLine()
		me.sbar.refresh_thumb()
		
	def activate(me,activate=True):
		if me.active==activate:
			return
		me.active=activate
		me.szr.Show(me.text,show=activate) 	
class MainPanel(wx.Panel):

	def __init__(me,parent):
		wx.Panel.__init__(me,parent,id=-1)
		
		szr=wx.BoxSizer(wx.HORIZONTAL)		me.SetSizer(szr)		me.SetAutoLayout(True)
		
		me.szr          = szr
		me.docs         = []
		me.virgin       = True
		me.doc_w_focus  = None

		
		me.new_document()
		
		if debug.on:
			me.open(debug.file1)
			me.open(debug.file2)
			me.open(debug.file3)
		
	def new_document(me):			
		doc=DocumentCtrl(me)
		me.docs.append(doc)kkkll
		
		doc.events.Unsaved+=me.on_unsaved
		doc.events.ActiveStateToggleRequest+=me.on_active_state_toggle_request
				
		me.szr.Add(doc,1,wx.EXPAND)		me.szr.Layout()
		
		me.make_active(doc)
		
		me._set_title()
	
	def yes_no_cancel(me,message,title='A Message Box'):
		d = wx.MessageDialog(me,message,title, wx.YES_NO | wx.YES_DEFAULT | wx.CANCEL | wx.ICON_INFORMATION)
		return d.ShowModal()
	
	def close_n_no_new_document(me):
		if me.doc_w_focus.modified and not debug.on:
			save=me.yes_no_cancel('Save file first?')
			if save==CANCEL:
				return CANCEL
			if save==YES:
				if me.doc_w_focus.save()<>OK:
					return CANCEL
		
		szr=me.GetSizer()
		szr.Detach(me.doc_w_focus)
		me.doc_w_focus.Destroy()
		me.doc_w_focus=None
		
		return OK
		
	def close(me):
		if me.close_n_no_new_document()<>OK:
			return CANCEL
		me.new_document()
		return OK
		
	def exit(me):
		if me.close_n_no_new_document()<>OK:
			return CANCEL
		return OK
		
	def open(me,file=None):
		#?? don't open if already open
		#if me.close()<>OK:return CANCEL

		if me.virgin and not me.doc_w_focus.modified:
			me.close_n_no_new_document()
			
		me.virgin=False
		
		if file is None:
			d=wx.FileDialog(me,message="Open file...",style=wx.OPEN)			if d.ShowModal()<>OK:
				return CANCEL
			file=d.GetPath()
			
		me.new_document()
		me.doc_w_focus.open(file)
		me._set_title()
		return OK
			
	def save(me):
		me.doc_w_focus.save()
		me._set_title()
	
	def save_as(me):
		me.doc_w_focus.save_as()
		me._set_title()
		
	def _set_title(me):
		title=''
		if debug.on:
			title+='[WARNING - debug is on] '
		if me.doc_w_focus.modified:
			title+='*** '
			
		if me.doc_w_focus.file==None:
			title+='Untitled'
		else:
			dir,file=os.path.split(me.doc_w_focus.file)
			
			title+=file
			
			ancestors=dir.split(os.sep) # ?? not kosher, does it break somewhere?
			i=-1
			if ancestors[i]=='':
				i-=1
			title+=' in '+ ancestors[i]
			i-=1
			try: title+=' > '+ ancestors[i]
			except IndexError:pass
		
		title+=' - Farmhand'
		me.GetParent().SetTitle(title)
		
	def make_active(me,doc):
		expanding   = doc
		collapsing  = me.doc_w_focus
 
 		expanding.activate(True)
		me.szr.GetItem(expanding).SetProportion(1)	
		
		if collapsing is not None:
			collapsing.activate(False)
			me.szr.GetItem(collapsing).SetProportion(0)
			
		me.szr.Layout()
		
		me.doc_w_focus=expanding
		me._set_title()
		
	# - document event handlers
	
	def on_unsaved(me,e):
		if e.src<>me.doc_w_focus: # can this happen?
			return
		me._set_title()

	def on_active_state_toggle_request(me,e):
		if len(me.docs)==1:
			return
			
		if e.src.active:
			assert e.src==me.doc_w_focus
		else:
			me.make_active(e.src)

		
	# - <document event handlers>

class MainFrame(wx.Frame):
	"""
	This class is responsible for menus, and toolbar.
	MainPanel handles title and status bar.
	"""

	def __init__(me,size=(700,700)):		
		wx.Frame.__init__(me,parent=None,title='Farmhand',size=size)
		me.main_panel=MainPanel(me)
		
		# - events
		
		me.Bind(wx.EVT_CLOSE,me.OnApplicationClose)
		
		# - menus
		
		def _local_menu_add(menu,label,handler,id,help_str="",kind=wx.ITEM_NORMAL):
			if label=='---':
				menu.AppendSeparator()
				return
			menu.Append(id,label,help_str,kind)
			me.Bind(wx.EVT_MENU,handler,id=id)
			
		menubar=wx.MenuBar()
		me.SetMenuBar(menubar)
		
		file_menu=wx.Menu()
		menubar.Append(file_menu,"&File")
		for       label,              handler,      id,             help_str in (
				( "&New\tCtrl-N",     me.OnNew,     wx.ID_NEW,     "Bust it open"      ),
				( "&Open...\tCtrl-O", me.OnOpen,    wx.ID_OPEN,    "Whip it out"       ),
				( "&Save\tCtrl-S",    me.OnSave,    wx.ID_SAVE,    "Save this mother"  ),
				( "Save As...",       me.OnSaveAs,  wx.ID_SAVEAS,  "Save file with a new name" ),
				# ?? 2do "Rename..."
				( "&Close\tCtrl-W",   me.OnClose,   wx.ID_CLOSE,   "Close file"        ),
				( "---",              '---',        '---',         '---'               ),
				( "&Exit\tCtrl-X",    me.OnExit,    wx.ID_EXIT,    "Quit"              )
				):
			_local_menu_add(file_menu,label,handler,id,help_str)
			
		edit_menu=wx.Menu()
		menubar.Append(edit_menu,"&Edit")
		for       label,              handler,      id in (
				( "&Undo\tCtrl-Z",    me.OnUndo,    wx.ID_UNDO     ),
				( "&Redo\tShift-Ctrl-Z",me.OnRedo,  wx.ID_REDO     ),
				( "---",              '---',        '---'          ),
				( "Cut\tCtrl-X",      me.OnCut,     wx.ID_CUT      ),
				( "&Copy\tCtrl-C",    me.OnCopy,    wx.ID_COPY     ),
				( "&Paste\tCtrl-V",   me.OnPaste,   wx.ID_PASTE    ),
				( "---",              '---',        '---'          ),
				( "&Find...\tCtrl-F", me.OnFind,    wx.ID_FIND     ),
				# ?? 2do "Find Again"
				):
			_local_menu_add(edit_menu,label,handler,id)
			
		# - </menus>
		
		me.SetStatusBar(wx.StatusBar(me,wx.ID_ANY))
	
	# - menu handlers
		
	# file menu	
	def OnNew(me,e):pass
	def OnOpen(me,e):me.main_panel.open()
	def OnSave(me,e): me.main_panel.save()
	def OnSaveAs(me,e): me.main_panel.save_as()
	def OnClose(me,e):me.main_panel.close()
	def OnExit(me,e):
		can_veto=True
		me.Close(can_veto)
		
	# edit menu
	def OnUndo(me,e):pass
	def OnRedo(me,e):pass
	def OnCut(me,e):pass
	def OnCopy(me,e):pass
	def OnPaste(me,e):pass
	def OnFind(me,e):pass
		
	# - </menu handlers>
	
	def OnApplicationClose(me,e):
		if me.main_panel.exit()<>OK:
			e.Veto()
			return
		me.Destroy()


def main():
	app = wx.PySimpleApp()	frame = MainFrame()
	frame.Show()
	frame.Centre()	app.MainLoop()



if __name__=="__main__":
	main()